﻿Imports System.IO
Imports System.IO.Path
Imports System.Reflection
Imports PowerSong.SongDatabase.Legacy.Items
Imports PowerSong.SongDatabase.Legacy.Logging

''' <summary>
''' Represents a PowerSong database, which ultimately comprises a folder containing files of various types.
''' </summary>
Public Class Database

    ''' <summary>
    ''' The version number of any database created or loaded by this class.
    ''' </summary>
    Public Const DATABASE_FILE_VERSION As String = "0.9.9"

    ''' <summary>
    ''' The minimum version of the database acceptable primarily when importing data from the web.
    ''' </summary>
    Public Const MINIMUM_FILE_VERSION As String = "0.9.6"

    ''' <summary>
    ''' The name of the default category.
    ''' </summary>
    Public Const DEFAULT_CATEGORY_NAME As String = "Default"

    ''' <summary>
    ''' Gets the location of the "InitialContent", which is contained in the folder containing the PowerSong executable.
    ''' This folder contains software-wide additional content, such as bible translations and plugins.
    ''' </summary>
    ''' <value>The location of the initial content folder.</value>
    Public Shared ReadOnly Property InitialContentFolder() As String
        Get
            Return My.Application.Info.DirectoryPath + "\Initial Content"
        End Get
    End Property

    Private FLocation As String = String.Empty

    ''' <summary>
    ''' Gets the location of the active database.
    ''' </summary>
    ''' <value>The location of the active database.</value>
    Public ReadOnly Property Location() As String
        Get
            Return FLocation
        End Get
    End Property

    Private FData As Data = Nothing

    Friend ReadOnly Property Data() As Data
        Get
            Return FData
        End Get
    End Property

    Private FSongs As Songs = Nothing

    ''' <summary>
    ''' Provides a mechanism by which songs can be manipulated.
    ''' </summary>
    ''' <value>The interface to song editing functionality.</value>
    Public ReadOnly Property Songs() As Songs
        Get
            Return FSongs
        End Get
    End Property

    Private FLicenses As Licenses = Nothing

    ''' <summary>
    ''' Provides a mechanism by which licenses can be manipulated.
    ''' </summary>
    ''' <value>The interface to license editing functionality.</value>
    Public ReadOnly Property Licenses() As Licenses
        Get
            Return FLicenses
        End Get
    End Property

    Private FStyles As Styles = Nothing

    ''' <summary>
    ''' Provides a mechanism by which styles can be manipulated.
    ''' </summary>
    ''' <value>The interface to style editing functionality.</value>
    Public ReadOnly Property Styles() As Styles
        Get
            Return FStyles
        End Get
    End Property

    Private FSettings As Settings = Nothing

    ''' <summary>
    ''' Provides a mechanism by which database-wide settings can be manipulated.
    ''' </summary>
    ''' <value>The interface to setting editing functionality.</value>
    Public ReadOnly Property Settings() As Settings
        Get
            Return FSettings
        End Get
    End Property

    Private FCategories As Categories = Nothing

    ''' <summary>
    ''' Provides a mechanism by which song categories can be manipulated.
    ''' </summary>
    ''' <value>The interface to song category editing functionality.</value>
    Public ReadOnly Property Categories() As Categories
        Get
            Return FCategories
        End Get
    End Property

    Private FLogging As ActivityLog = Nothing

    ''' <summary>
    ''' Provides a mechanism by which the activity log can be manipulated.
    ''' </summary>
    ''' <value>The interface to logging functionality.</value>
    Public ReadOnly Property Logging() As ActivityLog
        Get
            Return FLogging
        End Get
    End Property

    ''' <summary>
    ''' Gets the list of plugin file names.
    ''' </summary>
    ''' <value>The list of all the plugin files in the active database.</value>
    Public ReadOnly Property PluginFiles() As String()
        Get
            Dim Filenames As String() = Directory.GetFiles(FLocation, "*.plugin")
            For I As Integer = 0 To Filenames.Length - 1
                Filenames(I) = GetFileName(Filenames(I))
            Next
            Return Filenames
        End Get
    End Property

    ''' <summary>
    ''' Gets the version of the database.
    ''' </summary>
    ''' <value>The database version.</value>
    Public ReadOnly Property DatabaseVersion() As String
        Get
            Return Settings.GetSetting("DatabaseVersion")
        End Get
    End Property

    Private Sub New(ByVal databaseLocation As String)
        FLocation = databaseLocation
    End Sub

    ''' <summary>
    ''' Creates a new database at the specified location.
    ''' </summary>
    ''' <param name="location">An empty folder location in which to create the new database.</param>
    ''' <returns>The new database.</returns>
    Public Shared Function Create(ByVal location As String) As Database

        If Directory.GetFiles(location).Length <> 0 Then
            Throw New ApplicationException("A database cannot be created at the specified location because it is not empty. Please specify an empty folder in which to create a database.")
        End If

        Dim Result As New Database(location)

        ' Create a blank data file
        Result.FData = New Data
        Result.Save()

        ' Initialise the other classes
        Result.FSongs = New Songs(Result)
        Result.FStyles = New Styles(Result)
        Result.FSettings = New Settings(Result)
        Result.FCategories = New Categories(Result)
        Result.FLogging = New ActivityLog(Result)
        Result.FLicenses = New Licenses(Result)

        ' Add default settings
        Result.Settings.SetDefaultSettings()

        ' Add a default style
        Result.Styles.AddStyle(New Style("Default"))

        ' Add a default category
        Result.Categories.AddCategory(Guid.NewGuid, DEFAULT_CATEGORY_NAME)

        Return Result

    End Function

    Public Shared Function IsValidDatabase(ByVal location As String) As Boolean

        Try

            ' The database XML file itself must exist
            If Not File.Exists(location + "\Database.xml") Then Return False

            ' Check if database is valid
            Dim InterimData As New DataSet
            InterimData.ReadXml(location + "\Database.xml")
            If Not InterimData.Tables.Contains("Settings") Then Return False
            Dim SettingsTable As DataTable = InterimData.Tables("Settings")
            SettingsTable.PrimaryKey = New DataColumn() {SettingsTable.Columns("Name")}
            If Not SettingsTable.Rows.Contains("DatabaseVersion") Then Return False

            ' We got this far, so perhaps it is valid?
            Return True

        Catch ex As Exception
            Return False
        End Try

    End Function

    ''' <summary>
    ''' Loads the database at the specified location.
    ''' </summary>
    ''' <param name="location">The location of the database to load.</param>
    ''' <returns>The database at the specified location.</returns>
    Public Shared Function Load(ByVal location As String) As Database

        ' Check for a valid location
        If Not File.Exists(location + "\Database.xml") Then
            Throw New ApplicationException("The specified location is not a valid PowerSong database.")
        End If

        ' Check if database is valid
        Dim InterimData As New DataSet
        InterimData.ReadXml(location + "\Database.xml")
        If Not InterimData.Tables.Contains("Settings") Then Throw New ApplicationException("The database is not a valid PowerSong database.")
        Dim SettingsTable As DataTable = InterimData.Tables("Settings")
        SettingsTable.PrimaryKey = New DataColumn() {SettingsTable.Columns("Name")}
        If Not SettingsTable.Rows.Contains("DatabaseVersion") Then Throw New ApplicationException("The database is not a valid PowerSong database.")

        ' Upgrade the database if necessary
        DatabaseUpgrade.CheckDatabase(location, InterimData)
        InterimData.Dispose()

        ' Load the database now that it is guaranteed to work
        Dim Result As New Database(location)
        Result.FData = New Data
        Result.FData.ReadXml(location + "\Database.xml")

        ' Initialise the other classes
        Result.FSongs = New Songs(Result)
        Result.FStyles = New Styles(Result)
        Result.FSettings = New Settings(Result)
        Result.FCategories = New Categories(Result)
        Result.FLogging = New ActivityLog(Result)
        Result.FLicenses = New Licenses(Result)

        ' Check categories
        DatabaseUpgrade.CheckSongCategories(Result)

        Return Result

    End Function

    Private FIsInTransactionMode As Boolean = False

    Friend Sub StartTransaction()
        If FIsInTransactionMode Then Throw New Exception("A transaction has already been started.")
        FIsInTransactionMode = True
    End Sub

    Friend Sub EndTransaction()
        If Not FIsInTransactionMode Then Throw New Exception("There is currently no transaction.")
        FIsInTransactionMode = False
        Save()
    End Sub

    Friend Sub Save()
        FData.AcceptChanges()
        If Not FIsInTransactionMode Then FData.WriteXml(FLocation + "\Database.xml")
    End Sub

End Class
